from fastapi import HTTPException

import cache.keys
import log
import utils.auth_util
from auth.jwt_tools import generate_jwt_token
from schemas.auth import GetTokenResponse, AuthBaseInfo, RefreshTokenRequest, RefreshTokenResponse, LoginAppResponse, \
    LogoutAppResponse, UnregisterAppResponse
from service import ACCESS_TOKEN_EXPIRE_TIME, REFRESH_TOKEN_EXPIRE_TIME
import cache.redis_cache
import dao.auth_dao


def getUserToken(appId: str,
                 appName: str):
    redis = cache.redis_cache.get_redis_connection()
    if redis:
        try:
            flag = 0

            accessToken = redis.get(cache.keys.REDIS_CACHE_AUTH_ACCESS_TOKEN_PREFIX + appId)
            if accessToken:
                refreshToken = redis.get(cache.keys.REDIS_CACHE_AUTH_REFRESH_TOKEN_PREFIX + appId)
                if not refreshToken:
                    redis.delete(cache.keys.REDIS_CACHE_AUTH_ACCESS_TOKEN_PREFIX + appId)
                    flag = 1
                else:
                    log.logger.debug("use the old access/refresh token.")
            else:
                redis.delete(cache.keys.REDIS_CACHE_AUTH_REFRESH_TOKEN_PREFIX + appId)

                flag = 1

            if flag == 1:
                accessToken = generate_jwt_token(appId, appName, ACCESS_TOKEN_EXPIRE_TIME)
                refreshToken = generate_jwt_token(appId, appName, REFRESH_TOKEN_EXPIRE_TIME)

                redis.set(cache.keys.REDIS_CACHE_AUTH_ACCESS_TOKEN_PREFIX + appId, accessToken,
                          ex=ACCESS_TOKEN_EXPIRE_TIME * 60)
                redis.set(cache.keys.REDIS_CACHE_AUTH_REFRESH_TOKEN_PREFIX + appId, refreshToken,
                          ex=REFRESH_TOKEN_EXPIRE_TIME * 60)

            log.logger.debug("Obtain the user token from " + str(appId) + "(" + str(appName) + ")")

            return GetTokenResponse(accessToken=accessToken,
                                    refreshToken=refreshToken,
                                    accessTokenExpiredInMinute=ACCESS_TOKEN_EXPIRE_TIME,
                                    refreshTokenInMinute=REFRESH_TOKEN_EXPIRE_TIME,
                                    result='SUCCESS')
        except Exception as e:
            log.logger.error(e.__str__())

    raise HTTPException(status_code=500, detail="Obtain Token Failed")


def unregisterApp(appId: str, userId: str, userPass: str):
    result = dao.auth_dao.getAppAuthByAppIdUserId(appId, userId)
    if not result:
        raise HTTPException(status_code=404, detail="User Not Found")

    if utils.auth_util.md5Encode(userPass) != result['user_pass']:
        raise HTTPException(status_code=401, detail="Password Verified Failed")

    if dao.auth_dao.unregisterApp(appId, userId):
        redis = cache.redis_cache.get_redis_connection()
        if redis:
            try:
                key = appId + ":" + userId
                redis.delete(cache.keys.REDIS_CACHE_AUTH_ACCESS_TOKEN_PREFIX + key)
                redis.delete(cache.keys.REDIS_CACHE_AUTH_REFRESH_TOKEN_PREFIX + key)
            except Exception as e:
                raise HTTPException(status_code=500, detail=e.__str__())

            return UnregisterAppResponse(result='Unregistered Completed')
        else:
            raise HTTPException(status_code=500, detail="CACHE BUSY")
    else:
        raise HTTPException(status_code=500, detail="DB BUSY")


def loginApp(appId: str, userId: str, userPass: str):
    authInfo = getAppAuthInfo(appId)
    if not authInfo:
        raise HTTPException(status_code=404, detail="Auth Info Not Found")

    result = dao.auth_dao.getAppAuthByAppIdUserId(appId, userId)
    if not result:
        raise HTTPException(status_code=404, detail="User Not Found")

    if utils.auth_util.md5Encode(userPass) != result['user_pass']:
        raise HTTPException(status_code=401, detail="PASS VERIFIED FAILED")

    key = appId + ":" + userId
    token = getUserToken(key, authInfo.appName)
    return LoginAppResponse(result='Login Completed',
                            token=token.accessToken,
                            accessTokenExpiredInMinute=token.accessTokenExpiredInMinute)


def getAppAuthInfo(appId: str):
    try:
        result = dao.auth_dao.getAppAuthByAppId(appId)

        return AuthBaseInfo(appId=appId,
                            appSecret=result['app_secret'],
                            appName=result['app_name'],
                            source=result['source'])
    except Exception as e:
        log.logger.error(e.__str__())

    return AuthBaseInfo(appId='',
                        appSecret='',
                        appName='',
                        source=-1)


def registerApp(appId: str, userId: str, userPass: str):
    return dao.auth_dao.registerApp(appId, userId, utils.auth_util.md5Encode(userPass))


def invalidateTokens(appId: str):
    redis = cache.redis_cache.get_redis_connection()
    if redis:
        redis.delete(cache.keys.REDIS_CACHE_AUTH_ACCESS_TOKEN_PREFIX + appId)
        redis.delete(cache.keys.REDIS_CACHE_AUTH_REFRESH_TOKEN_PREFIX + appId)


def logoutApp(appId: str, userId: str, accessToken: str):
    redis = cache.redis_cache.get_redis_connection()
    if redis:
        try:
            key = appId + ":" + userId

            cachedAccessToken = redis.get(cache.keys.REDIS_CACHE_AUTH_ACCESS_TOKEN_PREFIX + key)
            if not cachedAccessToken or cachedAccessToken != accessToken:
                raise HTTPException(status_code=401, detail='Access Token Not Matched.')

            redis.delete(cache.keys.REDIS_CACHE_AUTH_ACCESS_TOKEN_PREFIX + key)
            redis.delete(cache.keys.REDIS_CACHE_AUTH_REFRESH_TOKEN_PREFIX + key)

            return LogoutAppResponse(result='Logout Completed')
        except Exception as e:
            log.logger.error(e.__str__())
            raise e


def refreshAuth(request: RefreshTokenRequest):
    try:
        redis = cache.redis_cache.get_redis_connection()
        if redis:
            accessToken = redis.get(cache.keys.REDIS_CACHE_AUTH_ACCESS_TOKEN_PREFIX + request.appId)
            if not accessToken:
                redis.delete(cache.keys.REDIS_CACHE_AUTH_REFRESH_TOKEN_PREFIX + request.appId)

                raise HTTPException(status_code=404, detail="FAILED,Old Access Token Not Found, Pls Get A New Token")

            cachedRefreshToken = redis.get(cache.keys.REDIS_CACHE_AUTH_REFRESH_TOKEN_PREFIX + request.appId)
            if cachedRefreshToken and cachedRefreshToken == request.refreshToken:
                redis.set(cache.keys.REDIS_CACHE_AUTH_ACCESS_TOKEN_PREFIX + request.appId, accessToken,
                          ex=ACCESS_TOKEN_EXPIRE_TIME * 60)

                return RefreshTokenResponse(accessToken=accessToken,
                                            accessTokenExpiredInMinute=ACCESS_TOKEN_EXPIRE_TIME,
                                            result='Refreshed Completed')
            else:
                redis.delete(cache.keys.REDIS_CACHE_AUTH_REFRESH_TOKEN_PREFIX + request.appId)
                redis.delete(cache.keys.REDIS_CACHE_AUTH_ACCESS_TOKEN_PREFIX + request.appId)

                raise HTTPException(status_code=404, detail="FAILED,Refresh Token Not Found/Not Matched,Get A New Token")
    except Exception as e:
        log.logger.error(e.__str__())
        raise e
